﻿#include "stdafx.h"
#if _QG_USE_D11
#include "qg_d11_dev.h"
#include "qg_d11_mtrl.h"
#include "qg_d11_draw.h"
#include "qg_trace.h"
#include "qg_math.h"
#include "qg/qgimage.h"

//////////////////////////////////////////////////////////////////////////
// D11 디바이스
KIMPL_SUBCLASS(D11Device, "RenderDeviceD11");

namespace _d11
{
	// 타입
	kType DevTypeD11()
	{
		return D11Device::Type();
	}

	// 2D 그리기 꼭지점 정보
	struct Vertex2D
	{
		tpl::Vec2	pos;
		tpl::Vec2	tex;
		kuint		color;

		void Set(float x, float y, float u, float v, kuint cc)
		{
			pos.Set(x, y);
			tex.Set(u, v);
			color = cc;
		}
	};

	//
	static kModule* ModuleD3D11 = NULL;
	static kModule*	ModuleD3DCOMPILER = NULL;
}

//
PFN_D3D11_CREATE_DEVICE D11Device::D3D11CREATEDEVICE = NULL;

pD3DCompile D11Device::D3DCOMPILE = NULL;
D11Device::pD3DReflect D11Device::D3DREFLECT = NULL;

//
void D11Device::TypeInitialize(kType type)
{
	_d11::ModuleD3D11 = k_mod_open("d3d11", NULL, 0);
	if (_d11::ModuleD3D11)
		k_mod_sym_func(_d11::ModuleD3D11, "D3D11CreateDevice", (kfunc*)&D3D11CREATEDEVICE);

	_d11::ModuleD3DCOMPILER = k_mod_open(D3DCOMPILER_DLL_A, NULL, 0);
	if (_d11::ModuleD3DCOMPILER)
	{
		k_mod_sym_func(_d11::ModuleD3DCOMPILER, "D3DCompile", (kfunc*)&D3DCOMPILE);
		k_mod_sym_func(_d11::ModuleD3DCOMPILER, "D3DReflect", (kfunc*)&D3DREFLECT);
	}
}

//
void D11Device::TypeFinalize(kType type)
{
	if (_d11::ModuleD3DCOMPILER)
		k_mod_close(_d11::ModuleD3DCOMPILER);

	if (_d11::ModuleD3D11)
		k_mod_close(_d11::ModuleD3D11);
}

//
D11Device::D11Device()
{
}

//
D11Device::~D11Device()
{
	//
	_Finalize();

	//
	_render.Dispose();

	if (_context)
	{
		_context->ClearState();
		_context->Release();
	}

	kunk_release(_device);
}

//
bool D11Device::_Initialize(qgStub* stub, kint flags)
{
	tpl::Point size(stub->GetSize());

	// 초기화 자료
	const D3D_DRIVER_TYPE c_drvtype[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	const D3D_FEATURE_LEVEL c_ftlevel[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
		/*
		: DX9 GPU 지금 누가 사겠음, 지원하려고 처리하는 것도 골아프고...
		: 근데 가상 머신이 출동하면?
		D3D_FEATURE_LEVEL_9_3,
		D3D_FEATURE_LEVEL_9_2,
		D3D_FEATURE_LEVEL_9_1
		*/
	};

	// D11 디바이스
	HRESULT hr;
	UINT dflg = 0;
	ksize_t n;

#if _SB_DEBUG_
	dflg |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	for (n = 0; n < K_COUNTOF(c_drvtype); n++)
	{
		hr = D3D11CREATEDEVICE(NULL, c_drvtype[n], NULL, dflg,
			c_ftlevel, (UINT)K_COUNTOF(c_ftlevel), D3D11_SDK_VERSION,
			&_device, &_feature_level, &_context);

		if (SUCCEEDED(hr))
		{
			_driver_type = c_drvtype[n];
			break;
		}
	}

	if (FAILED(hr))
		return false;

	// 개체 만들기
	IDXGIDevice* dxgi = NULL;
	IDXGIAdapter* adapter = NULL;
	IDXGIFactory* factory = NULL;

	if (FAILED(hr = _device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi)) ||
		FAILED(hr = dxgi->GetParent(__uuidof(IDXGIAdapter), (void**)&adapter)) ||
		FAILED(hr = adapter->GetParent(__uuidof(IDXGIFactory), (void**)&factory)))
	{
		// 헐 머임..
		kunk_release(factory);
		kunk_release(adapter);
		kunk_release(dxgi);

		return false;
	}

	// 디스플레이 모드
	_EnumDisplayMode(adapter);

	// 세이더 프로필 + 버전 별 기능 할당
	if (_feature_level >= D3D_FEATURE_LEVEL_11_0)
	{
		k_strcpy(_vprn.prof_vs, "vs_5_0");
		k_strcpy(_vprn.prof_ps, "ps_5_0");
		k_strcpy(_vprn.prof_gs, "gs_5_0");

		_caps.version_shader = 0x0500;
		_caps.max_texture_dimension = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}
	else if (_feature_level >= D3D_FEATURE_LEVEL_10_0)
	{
		if (_feature_level >= D3D_FEATURE_LEVEL_10_1)
		{
			k_strcpy(_vprn.prof_vs, "vs_4_1");
			k_strcpy(_vprn.prof_ps, "ps_4_1");
			k_strcpy(_vprn.prof_gs, "gs_4_1");
		}
		else
		{
			k_strcpy(_vprn.prof_vs, "vs_4_0");
			k_strcpy(_vprn.prof_ps, "ps_4_0");
			k_strcpy(_vprn.prof_gs, "gs_4_0");
		}

		_caps.version_shader = 0x0400;
		_caps.max_texture_dimension = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}
	else if (_feature_level >= D3D_FEATURE_LEVEL_9_3)
	{
		k_strcpy(_vprn.prof_vs, "vs_4_0_level_9_3");
		k_strcpy(_vprn.prof_ps, "ps_4_0_level_9_3");
		_vprn.prof_gs[0] = '\0';

		_caps.version_shader = 0x0300;
		_caps.max_texture_dimension = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}
	else
	{
		k_strcpy(_vprn.prof_vs, "vs_4_0_level_9_1");
		k_strcpy(_vprn.prof_ps, "ps_4_0_level_9_1");
		_vprn.prof_gs[0] = '\0';

		_caps.version_shader = 0x0200;
		_caps.max_texture_dimension = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}

	// CAPABILITIES. D11은 기능이 고정이라 그냥 정의함
	// 혹시 caps 관련 기능이 발견되면 수정여부 있음
	k_strcpy(_caps.renderer, "D11");
	k_strcpy(_caps.vendor, "Microsoft");

	_caps.supp_stencil = true;
	_caps.supp_multi_texture = true;
	_caps.supp_compressed_texture = true;
	_caps.supp_shader = true;
	_caps.supp_occlusion = true;
	_caps.supp_byte_glyph_texture = true;
	_caps.supp_texture_npo2 = true;

	_caps.max_texture_count = QGSGTEX_MAX_VALUE;	// 기본: 128
	_caps.max_offtex_count = QGSGOFF_MAX_VALUE;		// 기본: 8

	_caps.max_indices_count = K_MAX_INT - 1;
	_caps.max_gs_vertices_count = K_MAX_INT - 1;

	_caps.max_texture_LOD_bias = FLT_MAX;

	for (n = 0; n < 2; n++)
	{
		_caps.dim_alias_line[n] = 0;
		_caps.dim_alias_point[n] = 0;
		_caps.dim_smt_line[n] = 0;
		_caps.dim_smt_point[n] = 0;
	}

	_caps.version = D3D11_SDK_VERSION;

	_caps.flag_texture_image = QGIMGF_BGRA;

	// 멀티 샘플링
	if (!K_OFLAG(flags, QGSDF_MSAA))
	{
		_msaa_count = 1;
		_msaa_quality = 0;
	}
	else
	{
		hr = _device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, 4, &_msaa_quality);

		if (FAILED(hr))
		{
			// D11 스펙상 4X MSAA는 되야 정상. 안되는게 이상한 거임
			_msaa_count = 1;
			_msaa_quality = 0;
		}
		else
		{
			_msaa_count = 4;
			_msaa_quality--;
		}
	}

	// 스왑체인
	hr = _render.Create(factory, _device, _msaa_count, _msaa_quality, size.x, size.y,
		(HWND)stub->GetHandle(), K_OFLAG(flags, QGSDF_FULL) == 0);

	// 윗쪽 인터페이스 삭제
	factory->Release();
	adapter->Release();
	dxgi->Release();

	// 스왑체인 실패
	if (FAILED(hr))
		return false;

	// 정보 표시
	if (_screen.adt.IsHave())
	{
		const qgAdapterDesc& adt = _screen.adt[0];
		kobj_trace(KLV_INFO, "DirectX11 Driver");
		kobj_tracef(KLV_INFO, " - Driver: %s", adt.desc.Data());
		kobj_tracef(KLV_INFO, " - Dedicate video memory size is %lld", adt.mem);
		k_strncpy(_caps.vendor, adt.desc.Data(), 63);
	}

	// 슈퍼
	return super_type::_Initialize(stub, flags);
}

// 디스플레이 모드
bool D11Device::_EnumDisplayMode(IDXGIAdapter* adapter)
{
	HRESULT hr;

	// 스크린 모드
	IDXGIOutput* output;
	if (FAILED(hr = adapter->EnumOutputs(0, &output)))
		return false;

	UINT cnt = 0;

	if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &cnt, NULL)))
	{
		output->Release();
		return false;
	}

	DXGI_MODE_DESC* lst = k_new(cnt, DXGI_MODE_DESC);
	hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &cnt, lst);

	output->Release();

	if (FAILED(hr))
	{
		k_delete(lst);
		return false;
	}

	for (UINT i = 0; i < cnt; i++)
	{
		if (lst[i].RefreshRate.Denominator == 0)
			continue;

		qgScreenDesc scr;
		scr.dstn = 0;
		scr.width = lst[i].Width;
		scr.height = lst[i].Height;
		scr.freq = (kint)k_rint((float)lst[i].RefreshRate.Numerator / (float)lst[i].RefreshRate.Denominator);
		scr.internal_fmt = lst[i].Format;
		scr.internal_type = 0;
		_screen.scr.Add(scr);
	}

	k_delete(lst);

	// 어댑터...는 하나 밖에
	DXGI_ADAPTER_DESC adc;

	if (FAILED(hr = adapter->GetDesc(&adc)))
		return false;

	qgAdapterDesc adt;
	adt.index = 0;
	adt.mem = (klong)adc.DedicatedVideoMemory;
	k_conv_utf16_to_utf8(adt.desc.Data(), adt.desc.Maximum(), adc.Description, 0);
	adt.desc.Intern();
	_screen.adt.Add(adt);

	// 소트
	if (_screen.scr.IsHave())
	{
		tpl::HeapSort(_screen.scr.Data(), _screen.scr.Count(),
			[ ](const qgScreenDesc& l, const qgScreenDesc& r) throw() -> bool
		{
			return
				//l.freq > r.freq && 
				l.width > r.width ||
				l.height > r.height;
		});
	}

	return true;
}

// 리셋 또는 준비
bool D11Device::_ResetPrepare(const tpl::Point& size, bool prepare)
{
	// D11용으로 준비
	_sprn = &_vprn;
	_sdeb = &_vdeb;

	// 기본값
	super_type::_ResetPrepare(size, prepare);

	// TM, 디바이스에 달린 속성 처리, 나머지는 단위행렬로 초기화 되어 있음
	D11Device::SetTM(QGTMS_PROJ, tpl::Mat4::PerspectiveFovLH(KM_PI_HOH, size.Aspect(), 1.0f, 100000.0f));

	// 전용 내용 준비
	_vprn.m_shd_ps = NULL;
	_vprn.m_shd_vs = NULL;
	// 세이더 프로필은 앞서 했음

	// 인터페이스 준비
	if (prepare)
	{
		// 색깔 출력용 흰색 텍스쳐
		if ((_vdeb.texw = CreateTexture("d11_texw", 16, 16, 0, 1, QGRESF_DYNAMIC | QGTEXF_S2D, QGCF32_BGRA)) == NULL)
		{
			kobj_tracep(KLV_CRITICAL, KERR_FAIL_CREATE);
			return false;
		}

		qgTextureMap ms;
		if (_vdeb.texw->EnterMap(&ms, QGENTF_NONE))
		{
			memset(ms.data, 0xFF, ms.pitch * ms.height);
			_vdeb.texw->LeaveMap();
		}

		// 2D 그릴 레이아웃
		static qgLayoutElement s_le[2] =
		{
			{QGDLOU_POSITION, 0, QGDLOT_FLOAT4, 0, 0, },
			{QGDLOU_COLOR, 0, QGDLOT_UINTCOLOR, 0, 0, },
		};

		if ((_vdeb.lo = CreateLayout(K_COUNTOF(s_le), s_le)) == NULL)
		{
			kobj_tracep(KLV_CRITICAL, KERR_FAIL_CREATE);
			return false;
		}

		// 꼭지점 버퍼, 처음엔 8개(=사각형 꼭지점 개수 x2) 만 만든다
		if ((_vdeb.vb = CreateVertexBuffer(8, _vdeb.lo, 0, QGRESF_DYNAMIC)) == NULL)
		{
			kobj_tracep(KLV_CRITICAL, KERR_FAIL_CREATE);
			return false;
		}

		// 세이더
		static const char s_vs_tex[] = \
			"cbuffer ShaderConstants : register(b0)\n" \
			"{\n" \
			"	float4x4 sOrthoProj : packoffset(c0);\n" \
			"};\n" \
			"struct VSINPUT \n" \
			"{\n" \
			"	float4 pos : POSITION;\n" \
			"	float4 color : COLOR;\n" \
			"};\n" \
			"struct VSOUTPUT \n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"VSOUTPUT VS(VSINPUT i) \n" \
			"{\n" \
			"	VSOUTPUT o;\n" \
			"	o.pos = mul(sOrthoProj, float4(i.pos.xy, 0.0f, 1.0f));\n" \
			"	o.color = i.color;\n" \
			"	o.coord = i.pos.zw;\n" \
			"	return o;\n" \
			"}\n";
		static const char s_ps_tex[] = \
			"SamplerState splr0 : register(s0);\n" \
			"Texture2D<float4> tex0 : register(t0);\n" \
			"struct PSINPUT\n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"float4 PS(PSINPUT i) : SV_Target\n" \
			"{\n" \
			"	float4 t = tex0.Sample(splr0, i.coord);\n" \
			"	float4 c = t * i.color;\n" \
			"	if (c.a == 0.0f) discard;\n" \
			"	return saturate(c);\n"\
			"}\n";
		static const char s_ps_glyph[] = \
			"SamplerState splr0 : register(s0);\n" \
			"Texture2D<float> tex0 : register(t0);\n" \
			"struct PSINPUT\n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"float4 PS(PSINPUT i) : SV_Target\n" \
			"{\n" \
			"	float t = tex0.Sample(splr0, i.coord);\n" \
			"	if (t == 0.0f) discard;\n" \
			"	return (t * i.color.a) * float4(i.color.rgb, 1.0f);\n"\
			"}\n";
		static const char s_vs_textm[] = \
			"cbuffer ShaderConstants : register(b0)\n" \
			"{\n" \
			"	float4x4 sWorld : packoffset(c0);\n" \
			"	float4x4 sOrthoProj : packoffset(c4);\n" \
			"};\n" \
			"struct VSINPUT \n" \
			"{\n" \
			"	float4 pos : POSITION;\n" \
			"	float4 color : COLOR;\n" \
			"};\n" \
			"struct VSOUTPUT \n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"VSOUTPUT VS(VSINPUT i) \n" \
			"{\n" \
			"	VSOUTPUT o;\n" \
			"	o.pos = mul(mul(sWorld, sOrthoProj), float4(i.pos.xy, 0.0f, 1.0f));\n" \
			"	o.color = i.color;\n" \
			"	o.coord = i.pos.zw;\n" \
			"	return o;\n" \
			"}\n";

		// 그리기용 세이더
		_vdeb.sdraw = CreateShader("d11_draw_tex_2d");
		_vdeb.sdraw->BindData(QGSHADER_PS, s_ps_tex, sizeof(s_ps_tex), 0);
		_vdeb.sdraw->BindData(QGSHADER_VS, s_vs_tex, sizeof(s_vs_tex), 0);
		_vdeb.sdraw->Link();

		// 글자용 세이더
		_vdeb.sglyph = CreateShader("d11_draw_glyph");
		_vdeb.sglyph->BindData(QGSHADER_PS, s_ps_glyph, sizeof(s_ps_glyph), 0);
		_vdeb.sglyph->BindShader(QGSHADER_VS, "d11_draw_tex_2d");
		_vdeb.sglyph->Link();
	}

	// 그릴 수 있도록 함 (+뷰포트)
	_SetRenderTarget(NULL);

	return true;
}

// 빠이날
void D11Device::_Finalize()
{
	// 기본 오브젝트 제거
	kobj_unload(_vdeb.texw);
	kobj_unload(_vdeb.vb);
	kobj_unload(_vdeb.lo);
	kobj_unload(_vdeb.sdraw);
	kobj_unload(_vdeb.sglyph);

	//
	super_type::_Finalize();
}

// 레이아웃 처리
bool D11Device::Layout(const qgSevLayout& e)
{
	if (!super_type::Layout(e))
		return false;

	kGuardSpinLock(_dlock)
	{
		//
		if (FAILED(_render.Layout(_device, _msaa_count, _msaa_quality, e.width, e.height)))
		{
			// 스왑체인 레이아웃 실패
			// 오류 메시지는?!!
		}

		// 이 안에 기본 뷰포트가 들어 있음
		_render.Render(_context);
	}

	D11Device::SetTM(QGTMS_PROJ, tpl::Mat4::PerspectiveFovLH(KM_PI_HOH, _trfm.vp.aspect, 1.0f, 100000.0f));
	D11Device::SetTM(QGTMS_VIEW, tpl::Mat4::IdentifyMatrix());
	D11Device::SetTM(QGTMS_WORLD, tpl::Mat4::IdentifyMatrix());

	_InternalTrfmOTM(_vsize);

	return true;
}

//
bool D11Device::Enter(kint clearance)
{
	super_type::Enter(clearance);

	if (clearance&QGCLEAR_RENDER)
	{
		kGuardSpinLock(_dlock)
			_context->ClearRenderTargetView(_render.GetRtView(), _sprn->cr_bg.ToPointer());
	}

	if (clearance&(QGCLEAR_DEPTH | QGCLEAR_STENCIL))
	{
		UINT u = clearance & (D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL);

		kGuardSpinLock(_dlock)
			_context->ClearDepthStencilView(_render.GetDsView(), u, 1.0f, 0);
	}

	return true;
}

//
bool D11Device::Leave()
{
	return super_type::Leave();
}

//
bool D11Device::Flush()
{
	super_type::Flush();

	kGuardSpinLock(_dlock)
		_render.Flush();

	return true;
}

//
void D11Device::_InternalTrfmOTM(const tpl::Vec2& size)
{
	kGuardSpinLock(_dlock)
		_trfm.otm = tpl::Mat4::OrthoLH(size.width, size.height, 0.0f, 1.0f);
}

//
void D11Device::_InternalDraw(kint texcount, kint vertexcount, kint vertexstart)
{
	kGuardSpinLock(_dlock)
	{
		if (!_BindMtrl(texcount) ||
			!_BindShader() ||
			!_BindDraw(false))
		{
			// 설정 실패
			kobj_tracep(KLV_ERROR, KERR_INVAL);
		}
		else
		{
			// 그리기 함수 호출
			_context->Draw(vertexcount, vertexstart);
		}
	}
}

//
void D11Device::_InternalDrawIndexed(kint texcount, kint indexcount, kint vertexstart)
{
	kGuardSpinLock(_dlock)
	{
		if (!_BindMtrl(texcount) ||
			!_BindShader() ||
			!_BindDraw(true))
		{
			// 설정 실패
			kobj_tracep(KLV_ERROR, KERR_INVAL);
		}
		else
		{
			// 그리기 함수 호출
			_context->DrawIndexed(indexcount, _sprn->w_ioff, vertexstart);
		}
	}
}

//
void D11Device::Draw(kint vertexcount, kint vertexstart)
{
	QGTRACE_DEVICE(D11Device);

	if (!_sprn->m_shd || !_sprn->w_vb[0] || !_sprn->w_lo)
	{
		// 세이더 또는 꼭지점 없이 그리려 하다니!!!
		kobj_tracep(KLV_ERROR, KERR_ARGUMENT);
	}
	else
	{
		// 그리기
		_InternalDraw(/*_caps.max_texture_count*/ QGSGTEX_MAX_VALUE, vertexcount, vertexstart);
	}
}

//
void D11Device::DrawIndexed(kint indexcount, kint vertexstart)
{
	QGTRACE_DEVICE(D11Device);

	if (!_sprn->m_shd || !_sprn->w_vb[0] || !_sprn->w_ib || !_sprn->w_lo)
	{
		// 세이더 또는 꼭지점 또는 인덱스 없이 그리려 하다니!!!
		kobj_tracep(KLV_ERROR, KERR_ARGUMENT);
	}
	else
	{
		// 그리기
		_InternalDrawIndexed(/*_caps.max_texture_count*/ QGSGTEX_MAX_VALUE, indexcount, vertexstart);
	}
}

//
void D11Device::DrawGlyph(const tpl::Rect&bound, const tpl::ColorValue& color, qgTexture* tex)
{
	QGTRACE_DEVICE(D11Device);

	k_return_if_ok(color.a < 10);

	// 그리기 데이터 준비
	kuint bgra = color.ToBgra();
	tpl::Quad rt = bound.ToQuad().Center(_vsize.width, _vsize.y);
	const tpl::Quad& coord = (const tpl::Quad&)_math::Global::TexCoord;

	kGuardSpinLock(_dlock)
	{
		_d11::Vertex2D* vx = (_d11::Vertex2D*)_vdeb.vb->EnterMap(QGENTF_NONE);
		vx[0].Set(rt.left, rt.top, coord.left, coord.top, bgra);
		vx[1].Set(rt.right, rt.top, coord.right, coord.top, bgra);
		vx[2].Set(rt.left, rt.bottom, coord.left, coord.bottom, bgra);
		vx[3].Set(rt.right, rt.bottom, coord.right, coord.bottom, bgra);
		_vdeb.vb->LeaveMap();
	}

	// 그리기
	D11Device::SetRasz(_vdeb.rasz);
	D11Device::SetBlnd(_vdeb.bglyph);
	D11Device::SetSplr(0, _vdeb.splr);
	D11Device::SetTexture(0, tex ? tex : _vdeb.texw);
	D11Device::SetShader(_vdeb.sglyph);
	D11Device::SetLayout(_vdeb.lo);
	D11Device::SetVertexBuffer(0, _vdeb.vb);
	D11Device::SetTopology(QGTPG_TRI_STRIP);
	D11Device::_InternalDraw(1, 4, 0);
}

//
void D11Device::DrawRect(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex, const tpl::Quad* coord)
{
	QGTRACE_DEVICE(D11Device);

	k_return_if_ok(color.a < 10);

	if (!coord)
		coord = (const tpl::Quad*)&_math::Global::TexCoord;

	// 그리기 데이터
	kuint bgra = color.ToBgra();
	tpl::Quad rt = bound.ToQuad().Center(_vsize.width, _vsize.y);

	kGuardSpinLock(_dlock)
	{
		_d11::Vertex2D* vx = (_d11::Vertex2D*)_vdeb.vb->EnterMap(QGENTF_NONE);
		vx[0].Set(rt.left, rt.top, coord->left, coord->top, bgra);
		vx[1].Set(rt.right, rt.top, coord->right, coord->top, bgra);
		vx[2].Set(rt.left, rt.bottom, coord->left, coord->bottom, bgra);
		vx[3].Set(rt.right, rt.bottom, coord->right, coord->bottom, bgra);
		_vdeb.vb->LeaveMap();
	}

	// 그리기
	D11Device::SetRasz(_vdeb.rasz);
	D11Device::SetBlnd(_vdeb.blnd);
	D11Device::SetSplr(0, _vdeb.splr);
	D11Device::SetTexture(0, tex ? tex : _vdeb.texw);
	D11Device::SetShader(_vdeb.sdraw);
	D11Device::SetLayout(_vdeb.lo);
	D11Device::SetVertexBuffer(0, _vdeb.vb);
	D11Device::SetTopology(QGTPG_TRI_STRIP);
	D11Device::_InternalDraw(1, 4, 0);
}

//
void D11Device::DrawPolyLine(kint count, const tpl::Point* pts, const tpl::ColorValue* colors)
{
	QGTRACE_DEVICE(D11Device);

	// 확인
	k_return_if_fail(count >= 2);
	k_return_if_fail(pts);

	// 꼭지점 데이터 만들기
	kGuardSpinLock(_dlock)
	{
		if (_vdeb.vb->GetCount() <= count)
		{
			// 꼭지점 버퍼 개수 늘림
			if (_sprn->w_vb[0] == _vdeb.vb)
				_sprn->w_vb[0] = NULL;

			_vdeb.vb->Unload();

			if ((_vdeb.vb = CreateVertexBuffer(count + 1, _vdeb.lo, 0, QGRESF_DYNAMIC)) == NULL)
			{
				kobj_tracep(KLV_CRITICAL, KERR_FAIL_CREATE);
				_vdeb.vb = NULL;
				return;
			}
		}

		// 잠그고 넣기
		_d11::Vertex2D* vx = (_d11::Vertex2D*)_vdeb.vb->EnterMap(QGENTF_NONE);

		tpl::Vec2 h(_vsize.width / 2 - 0.5f, _vsize.height / 2 - 0.5f);
		float x, y;
		kint i;

		if (colors)
		{
			for (i = 0; i < count; i++)
			{
				x = -h.width + (float)pts[i].x;
				y = h.height - (float)pts[i].y;
				vx[i].Set(x, y, 0.0f, 0.0f, colors[i].ToBgra());
			}

			x = -h.width + (float)pts[0].x;
			y = h.height - (float)pts[0].y;
			vx[i].Set(x, y, 0.0f, 0.0f, colors[0].ToBgra());
		}
		else
		{
			for (i = 0; i < count; i++)
			{
				x = -h.width + (float)pts[i].x;
				y = h.height - (float)pts[i].y;
				vx[i].Set(x, y, 0.0f, 0.0f, 0xFFFFFFFF);
			}

			x = -h.width + (float)pts[0].x;
			y = h.height - (float)pts[0].y;
			vx[i].Set(x, y, 0.0f, 0.0f, 0xFFFFFFFF);
		}

		_vdeb.vb->LeaveMap();
	}

	// 그리기
	D11Device::SetRasz(_vdeb.rasz);
	D11Device::SetBlnd(_vdeb.blnd);
	D11Device::SetSplr(0, _vdeb.splr);
	D11Device::SetTexture(0, _vdeb.texw);
	D11Device::SetShader(_vdeb.sdraw);
	D11Device::SetLayout(_vdeb.lo);
	D11Device::SetVertexBuffer(0, _vdeb.vb);
	D11Device::SetTopology(QGTPG_LINE_STRIP);
	D11Device::_InternalDraw(1, count + 1, 0);
}

//
void D11Device::DrawPolyStrip(kint count, const tpl::Point* pts, const tpl::ColorValue* colors, qgTexture* tex, const tpl::Vec2* coords)
{
	QGTRACE_DEVICE(D11Device);

	// 확인
	k_return_if_fail(count >= 3);
	k_return_if_fail(pts);

	// 꼭지점 데이터 만들기
	kGuardSpinLock(_dlock)
	{
		if (_vdeb.vb->GetCount() <= count)
		{
			// 꼭지점 버퍼 개수 늘림
			if (_sprn->w_vb[0] == _vdeb.vb)
				_sprn->w_vb[0] = NULL;

			_vdeb.vb->Unload();

			if ((_vdeb.vb = CreateVertexBuffer(count + 1, _vdeb.lo, 0, QGRESF_DYNAMIC)) == NULL)
			{
				kobj_tracep(KLV_CRITICAL, KERR_FAIL_CREATE);
				_vdeb.vb = NULL;
				return;
			}
		}

		// 잠그고 넣기
		_d11::Vertex2D* vx = (_d11::Vertex2D*)_vdeb.vb->EnterMap(QGENTF_NONE);

		tpl::Vec2 h(_vsize.width / 2 - 0.5f, _vsize.height / 2 - 0.5f);
		float x, y;
		kint i;

		if (colors)
		{
			if (coords)
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + (float)pts[i].x;
					y = h.height - (float)pts[i].y;
					vx[i].Set(x, y, coords[i].u, coords[i].v, colors[i].ToBgra());
				}
			}
			else
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + (float)pts[i].x;
					y = h.height - (float)pts[i].y;
					vx[i].Set(x, y, 0.0f, 0.0f, colors[i].ToBgra());
				}
			}
		}
		else
		{
			if (coords)
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + (float)pts[i].x;
					y = h.height - (float)pts[i].y;
					vx[i].Set(x, y, coords[i].u, coords[i].v, 0xFFFFFFFF);
				}
			}
			else
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + (float)pts[i].x;
					y = h.height - (float)pts[i].y;
					vx[i].Set(x, y, 0.0f, 0.0f, 0xFFFFFFFF);
				}
			}
		}

		_vdeb.vb->LeaveMap();
	}

	// 그리기
	D11Device::SetRasz(_vdeb.rasz);
	D11Device::SetBlnd(_vdeb.blnd);
	D11Device::SetSplr(0, _vdeb.splr);
	D11Device::SetTexture(0, tex ? tex : _vdeb.texw);
	D11Device::SetShader(_vdeb.sdraw);
	D11Device::SetLayout(_vdeb.lo);
	D11Device::SetVertexBuffer(0, _vdeb.vb);
	D11Device::SetTopology(QGTPG_TRI_STRIP);
	D11Device::_InternalDraw(1, count, 0);
}

// 렌더 타깃 리셋
bool D11Device::_SetRenderTarget(qgTexture* tex)
{
	tpl::Point size;

	if (tex)
	{
		D11TextureOff* dtex = (D11TextureOff*)tex;

		size.Set(dtex->_width, dtex->_height);
	}
	else
	{
		// 화면 그리기 모드로
		_render.Render(_context);

		size = _trfm.sd;
	}

	_vsize.Set((float)size.width, (float)size.height);

	_trfm.vp.loc.Set(0, 0);
	_trfm.vp.size = size;

	_InternalTrfmOTM(_vsize);

	return true;
}

#endif	// _QG_USE_D11
